/*
 * Copyright 2007 National Bioinformatics Network
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package dgbgui.gui.event;

import java.awt.Color;
import java.awt.event.InputEvent;
import java.awt.geom.Point2D;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

import dgbgui.gui.widgets.Widget;
import dgbgui.gui.widgets.visual.QueryWidget;
import dgbgui.gui.widgets.visual.RectangleWidget;
import dgbgui.gui.widgets.visual.results.ConceptronInstanceResultWidget;
import dgbgui.gui.widgets.visual.results.ConceptronTypeResultWidget;
import dgbgui.gui.widgets.visual.results.ResultWidget;
import dgbgui.gui.widgets.visual.results.ResultsWidget;
import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.event.PDragSequenceEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.event.PInputEventFilter;
import edu.umd.cs.piccolo.util.PBounds;

public class GroupSelectionEventHandler extends PDragSequenceEventHandler 
implements CheckConsumedInputEventHandler {

    public GroupSelectionEventHandler() {
        setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK));
    }


    protected void startDrag(PInputEvent e) {
        super.startDrag(e);
        m_dragRectangle = new RectangleWidget(
                new Widget(e.getCamera()),
                e.getCanvasPosition().getX(),
                e.getCanvasPosition().getY(),
                0.0,
                0.0);
        m_dragRectangle.setFillColor(new Color(255,255,0,100));
        m_processed = true;
        m_camera = e.getCamera();
        m_startCanvasPoint = e.getCanvasPosition();

    }

    protected void drag(PInputEvent e) {
        super.drag(e);

        m_endCanvasPoint = e.getCanvasPosition();
        // Normalisation is required for settignt the bounds
        double x = Math.min(m_startCanvasPoint.getX(), m_endCanvasPoint.getX());
        double y = Math.min(m_startCanvasPoint.getY(), m_endCanvasPoint.getY());
        double width = Math.abs(m_startCanvasPoint.getX() - m_endCanvasPoint.getX());
        double height = Math.abs(m_startCanvasPoint.getY() - m_endCanvasPoint.getY());

        m_dragRectangle.setBounds(x,y,width,height);
        m_processed = true;
    }


    /**
     * @param nodes The nodes to check for containment in this group selection
     * @return The widgets that are within thebounds 
     */
    private Set<Widget> filterOnBounds(Set<Widget> widgets) {
        Set<Widget> filtered = new HashSet<Widget>();

        PBounds selectionBounds = m_dragRectangle.getBounds();
        m_camera.localToView(selectionBounds);
        //System.out.println("--- Selection" + selectionBounds.toString());
        for ( Object obj : widgets ) {
            if ( obj instanceof Widget) {
                Widget widget = (Widget)obj;
                PBounds widgetBounds = widget.getFullBounds();
                Widget parent = widget.getParentOfClass(ResultsWidget.class);
                if ( parent == null ) {
                    parent = widget.getParentOfClass(QueryWidget.class);
                }
                if ( parent != null ) {
                    // TODO: Can we do this more generically?
                    if ( widget instanceof ResultWidget ) {
                        parent.localToGlobal(widgetBounds);
                    }
                    if ( selectionBounds.contains(widgetBounds) ) {
                        filtered.add(widget);
                    }
                }
            }
        }
        return filtered;
    }

    @SuppressWarnings("unchecked")
    protected void endDrag(PInputEvent event) {
        super.endDrag(event);
        m_dragRectangle.removeFromParent();
        m_processed = true;

        if ( ( m_dragRectangle.getWidth() > 3 ) && 
             ( m_dragRectangle.getHeight() > 3 ) ) {

            // Determine the selection
            // For now, just do queries
            LinkedHashSet<Class> objectPreference = getObjectPreference();

            // Get all the nodes that the camera can see
            Collection<PNode> nodes = new HashSet<PNode>();
            for ( int k = 0; k < event.getCamera().getLayerCount(); ++k ) {
                nodes.addAll(event.getCamera().getLayer(k).getAllNodes(null,null));
            }
            Set<Widget> unfiltered = new HashSet<Widget>();
            for ( Object obj : nodes ) {
                if ( obj instanceof Widget) {
                    unfiltered.add((Widget)obj);
                }
            }

            // Filter widgets that are within the selection boundaries
            Set<Widget> filtered = filterOnBounds(unfiltered);
            filtered = Widget.findPreferencialObjects(filtered,objectPreference);
            if ( filtered.size() > 0 ) {
                SelectionConstructor.setSelection(event,filtered);
            }
        }

    }
    
    public LinkedHashSet<Class> getObjectPreference() {
        LinkedHashSet<Class> objectPreference = new LinkedHashSet<Class>();
        objectPreference.add(QueryWidget.class);
        objectPreference.add(ConceptronTypeResultWidget.class);
        objectPreference.add(ConceptronInstanceResultWidget.class);
        return objectPreference;
    }


    public boolean eventProcessed(PInputEvent event, int type) {
        m_processed = false;
        processEvent(event, type);
        return m_processed;
    }

    boolean m_processed;
    Point2D m_startCanvasPoint,m_endCanvasPoint;
    Widget m_dragRectangle;
    PCamera m_camera;


}
